﻿using System;
using System.IO.Ports;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
using OpenTap.Plugins.Interfaces.LedAnalyzer;

namespace OpenTap.Plugins.LedAnalyzer
{
    [Display("CatLedAnalyzer", Group: "LED Analyzer", Description: "Cat LED Analyzer driver")]
    public class CatLedAnalyzer : Instrument, ILedAnalyzer
    {

        #region Settings
        [Display("ComPort", Description: "The serial port ID of Cat", Order: 1)]
        [AvailableValues("AvailablePorts")]
        public string ComPort { get; set; }
        [Display("BaudRate", Description: "The serial port BaudRate of Cat ", Order: 2)]
        public EBaudRate BaudRate { get; set; }
        [Display("DataBits", Description: "The serial port DataBits of Cat ", Order: 3)]
        public EDataBits DataBits { get; set; }
        [Display("Parity", Description: "The serial port Parity of Cat ", Order: 4)]
        public Parity ParityCfg { get; set; }
        [Display("StopBits", Description: "The serial port StopBits of Cat ", Order: 5)]
        public StopBits StopBitsCfg { get; set; }
        [Display("TimeOutMs", Description: "The serial port TimeOut Ms ", Order: 7)]
        public int TimeOutMs { get; set; }
        #endregion
        protected bool IsInitialized;
        private SerialPort _serialConnection;
        public virtual SerialPort CatSerial
        {
            get { return _serialConnection ?? (_serialConnection = new SerialPort()); }
        }
        [Browsable(false)]
        public string[] AvailablePorts { get; set; }
        public CatLedAnalyzer()
        {
            AvailablePorts = SerialPort.GetPortNames();
            TimeOutMs = 800;
            Name = "Cat";
        }
        public override void Open()
        {
            base.Open();
            Initialize();
        }
        public override void Close()
        {
            if (IsInitialized)
            {
                CatSerial.Close();
                IsInitialized = false;
            }
            Log.Info("Cat led Sensor closed!");
            base.Close();
        }
        public void Initialize()
        {
            if (IsInitialized) throw new InvalidOperationException("Cat led sensor is already initialized!");
            Log.Info("Initializing Cat led Sensor....");
            try
            {
                CatSerial.PortName = ComPort;
                CatSerial.BaudRate = (int)BaudRate;
                CatSerial.Parity = ParityCfg;
                CatSerial.DataBits = (int)DataBits;
                CatSerial.StopBits = StopBitsCfg;
                CatSerial.ReadTimeout = TimeOutMs;
                CatSerial.Open();
            }
            catch (Exception ex)
            {
                Log.Error("Initializing Cat led sensor failed: " + ex);
                IsInitialized = false;
                throw;
            }

            IsInitialized = true;
            Log.Info("Initializing Cat led sensor done.");
        }

        public void CaptureData(EBrightnessLevel brightnessLevel)
        {
            Log.Info("Cat capture LED data start.");
            string command;
            if (brightnessLevel == EBrightnessLevel.Auto) command = "SetDefault 01";
            else if ((int)brightnessLevel>3) command = "SetDefault 03";
            else command = "SetDefault " + String.Format("{0:D2}", (int)brightnessLevel);
            string response;
            try
            {
                response = ExecuteCatCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Capture Cat Data failed: " + ex);
                throw;
            }
            if (!response.EndsWith("OK\n", StringComparison.InvariantCultureIgnoreCase))
                throw new ApplicationException("Capture command error");
        }
        public void CaptureDataPwm(EBrightnessLevel brightnessLevel, int averageFactor = 0)
        {
            if (averageFactor > 15 || averageFactor < 0)
                throw new ArgumentException("AverageFactor should be between 1~15");

            Log.Info( "Cat capture LED PWM data start.");
            var command = "Get CH00.PWM";
            if (averageFactor != 0)
                command = "Get CH" + String.Format("{0:D2}", averageFactor) + ".PWM";

            string response;
            try
            {
                response = ExecuteCatCommand(command, TimeOutMs);
                if (!response.EndsWith("\n", StringComparison.InvariantCultureIgnoreCase))  throw new ApplicationException("Capture command error");
                else
                {
                    response = response.Substring(1, response.Length - 2);
                    var subrespArray = response.Split(';');
                    var respArray0 = subrespArray[0].Split(',');
                    var red = Convert.ToByte(respArray0[0], CultureInfo.InvariantCulture);
                    var green = Convert.ToByte(respArray0[1], CultureInfo.InvariantCulture);
                    var blue = Convert.ToByte(respArray0[2], CultureInfo.InvariantCulture);
                    var respArray1 = subrespArray[1].Split(',');
                    var hue = Convert.ToInt32(respArray1[0], CultureInfo.InvariantCulture);
                    var saturation = Convert.ToInt32(respArray1[1], CultureInfo.InvariantCulture);
                    var intensity = Convert.ToInt32(respArray1[2], CultureInfo.InvariantCulture);
                    var blink = subrespArray[2];
                    Log.Info("Red: " + red + ", Green: " + green + ", Blue: " + blue + ".");
                    Log.Info("Hue: " + hue + ", Saturation: " + saturation + ", Intensity: " + intensity + ", blinking: " + blink + " .");
                }

            }
            catch (Exception ex)
            {
                Log.Error("Capture Cat Data Pwm failed: " + ex);
                //IsInitialized = false;
            }


        }
        public RgbData GetRgb(int channel)
        {
            if (channel < 0 || channel > 15)
                throw new ArgumentException("Channel of fiber should be 0~17");

            Log.Info( "Cat read LED RGB result.");
            var command = "Get CH" + String.Format("{0:D2}", channel) + ".RGB";
            string response;
            try
            {
                response = ExecuteCatCommand(command, TimeOutMs);
                response = response.Substring(1, response.Length - 2);
            }
            catch (Exception ex)
            {
                Log.Error("Get Cat Rgb Data failed: " + ex);
                throw;
            }
            var respArray = response.Split(',');
            var rgbData = new RgbData
            {
                Red = Convert.ToByte(respArray[0], CultureInfo.InvariantCulture),
                Green = Convert.ToByte(respArray[1], CultureInfo.InvariantCulture),
                Blue = Convert.ToByte(respArray[2], CultureInfo.InvariantCulture)
            };
            Log.Info("Red: " + rgbData.Red + ", Green: " + rgbData.Green + ", Blue: " + rgbData.Blue + ".");
            return rgbData;
        }

        public List<RgbData> GetRgbs()
        {
            throw new NotImplementedException();
        }

        public HsiData GetHsi(int channel)
        {
            if (channel < 0 || channel > 15)
                throw new ArgumentException("Channel of fiber should be 0~17");

            Log.Info( "Cat read LED HSI result.");
            var command = "Get CH" + String.Format("{0:D2}", channel) + ".HSI2";
            string response;
            try
            {
                response = ExecuteCatCommand(command, TimeOutMs);
                response = response.Substring(1, response.Length - 2);
            }
            catch (Exception ex)
            {
                Log.Error("Get Cat Hsi Data failed: " + ex);
                //IsInitialized = false;
                throw;
            }
            var respArray = response.Split(',');
            var hsiData = new HsiData
            {
                Hue = Convert.ToDouble(respArray[0], CultureInfo.InvariantCulture),
                Saturation = Convert.ToInt32(respArray[1], CultureInfo.InvariantCulture),
                Intensity = Convert.ToInt32(respArray[2], CultureInfo.InvariantCulture)
            };
            Log.Info( "Hue: " + hsiData.Hue + ", Saturation: " +
                hsiData.Saturation + ", Intensity: " + hsiData.Intensity + ".");
            return hsiData;
        }
        public List<HsiData> GetHsiGroup(int channelgroup)
        {
            if (channelgroup < 0 || channelgroup > 1)
                throw new ArgumentException("Group of Channel of fiber should be 0 or 1 (only for Cat)!");

            Log.Info("Cat read LED HSI result.");
            var command = "Get HSI.A" + channelgroup;
            string response;
            var HsiDataList = new List<HsiData>();

            try
            {
                response = ExecuteCatCommand(command, TimeOutMs);
                response = response.Substring(1, response.Length - 3);
            }
            catch (Exception ex)
            {
                Log.Error("Get Cat Hsi Data failed: " + ex);
                throw;
            }
            var respArray = response.Split(';');
            foreach (var channelresp in respArray)
            {
                var subresp = channelresp.Split(',');
                var hsiData = new HsiData
                {
                Hue = Convert.ToDouble(subresp[0], CultureInfo.InvariantCulture),
                Saturation = Convert.ToInt32(subresp[1], CultureInfo.InvariantCulture),
                Intensity = Convert.ToInt32(subresp[2], CultureInfo.InvariantCulture)
                };
                HsiDataList.Add(hsiData);
                Log.Info("Hue: " + hsiData.Hue + ", Saturation: " + hsiData.Saturation + ", Intensity: " + hsiData.Intensity + ".");
            }
            return HsiDataList;
        }

        private string ExecuteCatCommand(string command, int timeOutMs)
        {
            if (CatSerial == null || !IsInitialized)
                    throw new ApplicationException("Rs232 interface not initialized");
            if (!command.EndsWith("\r"))
                command = command + "\r";
            Log.Info( "Cat command: " + command.Substring(0, command.Length - 1));
            CatSerial.DiscardInBuffer();
            CatSerial.DiscardOutBuffer();
            CatSerial.Write(command);
            var response = "";
            var recTimer = new Stopwatch();
            recTimer.Start();
            while (true)
            {
                response = response + CatSerial.ReadExisting();
                if (response.EndsWith("\n"))
                {
                    Log.Info("Cat response: " + response.Substring(0, response.Length - 1));
                    return response;
                }
                if (recTimer.ElapsedMilliseconds > timeOutMs)
                    throw new ApplicationException("Cat command " + command + " timeout.");
            }
        }

        public ChromaticityData GetChromaticity(int channel)
        {
            throw new NotImplementedException();
        }

        public void Initialize(JObject configElement)
        {
            throw new NotImplementedException();
        }
    }
}
